package database

import (
	"abdis/config"
	"abdis/interface/redis"
	"abdis/lib/logger"
	"abdis/redis/protocol"
	"fmt"
	"os"
	"runtime"
	"strconv"
	"strings"
	"time"
)

const (
	server   = "server"
	client   = "client"
	cluster  = "cluster"
	keyspace = "keyspace"
)

func Ping(args [][]byte) redis.Reply {
	if len(args) == 0 {
		return protocol.MakePongReply()
	} else if len(args) == 1 {
		return protocol.MakeStatusReply(string(args[0]))
	} else {
		return protocol.MakeErrReply("ERR wrong number of argument for ping command")
	}
}

func Info(db *Server, args [][]byte) redis.Reply {
	if len(args) == 0 {
		infoCommandList := []string{server, client, cluster, keyspace}
		allSection := make([]byte, 0)
		for _, s := range infoCommandList {
			allSection = append(allSection, GetAbdisInfoString(s, db)...)
		}
		return protocol.MakeBulkReply(allSection)
	} else {
		logger.Info("args is " + string(args[0]))
		section := strings.ToLower(string(args[0]))
		switch section {
		case server:
			return protocol.MakeBulkReply(GetAbdisInfoString(server, db))
			//default:
			//	return protocol.MakeErrReply("Invalid section for 'info' command")
		}
	}

	return protocol.MakeArgNumErrReply("info")
}

func Auth(c redis.Connection, args [][]byte) redis.Reply {
	if len(args) != 1 {
		return protocol.MakeErrReply("ERR wrong number of arguments")
	}

	if config.Properties.RequirePass != "" {
		passwd := string(args[0])
		c.Auth(passwd)
		if config.Properties.RequirePass != passwd {
			return protocol.MakeErrReply("ERR invalid password")
		}
	}
	return protocol.MakeOkReply()
}

func DBSize(c redis.Connection, db *Server) redis.Reply {
	keys, _ := db.GetDBSize(c.GetDBIndex())
	return protocol.MakeIntReply(int64(keys))
}

func GetAbdisInfoString(section string, db *Server) []byte {
	logger.Info("section is " + section)
	//startUpTimeFromNow := getGodisRuninngTime()
	switch section {
	case "server":
		m := make(map[string]string)

		m["redis_version"] = config.Properties.Version
		m["os"] = runtime.GOOS
		m["process_id"] = strconv.Itoa(os.Getpid())

		// 获取内存统计信息
		var memStats runtime.MemStats
		runtime.ReadMemStats(&memStats)
		m["used_memory_human"] = fmt.Sprintf("%vM", memStats.Alloc/1024/1024)
		m["used_memory_peak_human"] = fmt.Sprintf("%vM", memStats.TotalAlloc/1024/1024)

		s := "# Server" + protocol.CRLF
		for key, value := range m {
			s += fmt.Sprintf("%s:%s"+protocol.CRLF, key, value)
		}
		return []byte(s)
		//		s := fmt.Sprintf("# Server\r\n"+
		//			"godis_version:%s\r\n"+
		//			"godis_mode:%s\r\n"+
		//			"os:%s %s\r\n"+
		//			"arch_bits:%d\r\n"+
		//			//"multiplexing_api:%s\r\n"+
		//			"go_version:%s\r\n"+
		//			"process_id:%d\r\n"+
		//			"run_id:%s\r\n"+
		//			"tcp_port:%d\r\n"+
		//			"uptime_in_seconds:%d\r\n"+
		//			"uptime_in_days:%d\r\n"+
		//			//"hz:%d\r\n"+
		//			//"lru_clock:%d\r\n"+
		//			"config_file:%s\r\n",
		//			godisVersion,
		//			//TODO,
		//			//TODO,
		//			//TODO,
		//			getGodisRunningMode(),
		//			runtime.GOOS, runtime.GOARCH,
		//			32<<(^uint(0)>>63),
		//			//TODO,
		//			runtime.Version(),
		//			os.Getpid(),
		//			config.Properties.RunID,
		//			config.Properties.Port,
		//			startUpTimeFromNow,
		//			startUpTimeFromNow/time.Duration(3600*24),
		//			//TODO,
		//			//TODO,
		//			config.Properties.CfPath)
		//		return []byte(s)
		//	case "client":
		//		s := fmt.Sprintf("# Clients\r\n"+
		//			"connected_clients:%d\r\n",
		//			//"client_recent_max_input_buffer:%d\r\n"+
		//			//"client_recent_max_output_buffer:%d\r\n"+
		//			//"blocked_clients:%d\n",
		//			tcp.ClientCounter,
		//			//TODO,
		//			//TODO,
		//			//TODO,
		//		)
		//		return []byte(s)
		//	case "cluster":
		//		if getGodisRunningMode() == config.ClusterMode {
		//			s := fmt.Sprintf("# Cluster\r\n"+
		//				"cluster_enabled:%s\r\n",
		//				"1",
		//			)
		//			return []byte(s)
		//		} else {
		//			s := fmt.Sprintf("# Cluster\r\n"+
		//				"cluster_enabled:%s\r\n",
		//				"0",
		//			)
		//			return []byte(s)
		//		}
		//	case "keyspace":
		//		dbCount := config.Properties.Databases
		//		var serv []byte
		//		for i := 0; i < dbCount; i++ {
		//			keys, expiresKeys := db.GetDBSize(i)
		//			if keys != 0 {
		//				ttlSampleAverage := db.GetAvgTTL(i, 20)
		//				serv = append(serv, getDbSize(i, keys, expiresKeys, ttlSampleAverage)...)
		//			}
		//		}
		//		prefix := []byte("# Keyspace\r\n")
		//		keyspaceInfo := append(prefix, serv...)
		//		return keyspaceInfo
	}
	return []byte("")
}

func RewriteAOF(db *Server) redis.Reply {
	err := db.persister.Rewrite()
	if err != nil {
		return protocol.MakeErrReply(err.Error())
	}
	return protocol.MakeOkReply()
}

// getGodisRuninngTime return the running time of godis
func getGodisRuninngTime() time.Duration {
	return time.Since(config.EachTimeServerInfo.StartUpTime) / time.Second
}
